﻿// (c) Copyright Michael Antonio.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved
using System;
using System.Collections.Generic;
using System.Diagnostics;
#if SILVERLIGHT
using System.Windows;
using System.Windows.Media;
#endif

namespace MichMan.Utilities
{
#if SILVERLIGHT
    public static class DependencyObjectExtensions
    {
        /// <summary>
        /// Breadth first search.  Returns first descendent of the parent that is of type TSource and matches the predicate.
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="parent"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static TSource GetFirstDescendant<TSource>(this DependencyObject parent) where TSource : DependencyObject
        {
            return GetFirstDescendant<TSource>(parent, c => true);
        }

        public static TSource GetFirstDescendant<TSource>(this DependencyObject parent, Func<TSource, bool> predicate) where TSource : DependencyObject
        {
            List<DependencyObject> queue = new List<DependencyObject>();
            queue.Add(parent);

            while (queue.Count != 0)
            {
                DependencyObject first = queue[0];
                // Debug.WriteLine("DO Type: " + first.GetType().ToString());
                queue.RemoveAt(0);
                if (first is TSource && predicate((TSource)first))
                {
                    return (TSource)first;
                }
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(first); i++)
                {
                    var child = VisualTreeHelper.GetChild(first, i);
                    queue.Add(child);
                }
            }
            return null;
        }

        public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject parent)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                yield return child;

                foreach (var descendents in GetDescendants(VisualTreeHelper.GetChild(parent, i)))
                {
                    yield return descendents;
                }
            }
        }

        public static IEnumerable<DependencyObject> GetChildren(this DependencyObject parent)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
            {
                yield return VisualTreeHelper.GetChild(parent, i);
            }
        }
    }

    /// <summary>
    /// Finds and caches a dependency object.  Useful for objects that have yet to be created, or where finding is expensive.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Finder<T> : Getter<T> where T : DependencyObject
    {
        public Finder()
            : this(depObj => true)
        {
        }

        public Finder(Func<T, bool> predicate)
            : this(depObj => true, null)
        {
        }

        public Finder(DependencyObject root)
            : base(() => GetFrom(depObj => true, root))
        {
        }

        public Finder(Func<T, bool> predicate, DependencyObject root)
            : base(() => GetFrom(predicate, root))
        {
        }

        private static T GetFrom(Func<T, bool> predicate, DependencyObject root)
        {
            if (predicate == null)
            {
                predicate = depObj => true;
            }

            if (root == null && Application.Current != null)
            {
                root = Application.Current.RootVisual;
            }

            if (root != null)
            {
                return root.GetFirstDescendant<T>(predicate);
            }

            return null;
        }
    }
#endif // SILVERLIGHT
}
